package cn.com.franke.other;

import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.hash.*;
import com.google.common.math.BigIntegerMath;
import com.google.common.math.DoubleMath;
import com.google.common.math.IntMath;
import com.google.common.math.LongMath;
import com.google.common.net.HostAndPort;
import com.google.common.net.HttpHeaders;
import com.google.common.net.InetAddresses;
import org.junit.Test;

import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.InetAddress;
import java.util.List;

import static java.math.RoundingMode.CEILING;
import static java.math.RoundingMode.FLOOR;

/**
 * Created by liuxianzhao on 2017/5/4.
 */
public class OtherTest {
    @Test
    public void testNet() {
//        介绍guava的net包中提供的HttpHeaders类，HostAndPort类以及InetAddresses的使用
//        guava中的net包目前提供的功能较少，而且大多类都标注了@Beta的注解，在guava中标记Beta注解表示这个类还不稳定，有可能在以后的版本中变化，或者去掉，所以不建议大量使用，这里也是只做简单的介绍。
//        先介绍下唯一一个没有Beta注解的类HttpHeaders，这个类中并没有实质的方法，只是定义了一些Http头名称的常量，通常如果需要我们会自己定义这些常量，如果你引用了guava包，那么就不再建议我们自己定义这些头名称的常量了，直接用它定义的即可。
//        这里面应该有几乎所有的Http头名称，例如：X_FORWARDED_FOR，UPGRADE，REFERER等等。用法也没有必要介绍了，直接引用常量就可以了。
//        再介绍下一个比较常用的小功能，有时候我们需要在配置文件中配置IP+端口，这时候需要自己写解析ip，端口的方法，guava为我们提供了解析类，我们看下用法实例：
        System.out.println(HttpHeaders.X_FORWARDED_FOR);
        System.out.println(HttpHeaders.CONTENT_TYPE);

        HostAndPort hostAndPort = HostAndPort.fromString("127.0.0.1:8080");
        System.out.println("host == " + hostAndPort.getHost());
        System.out.println("port == " + hostAndPort.getPortOrDefault(80));
//        HostAndPort类的静态方法fromString(String)可以解析出字符串中定义的Host和端口信息。
//        另外guava包中还提供了InetAddresses类，这个类是InetAddress的帮助类，通过这个类可以方便的从字符串中解析出InetAddress类。但是此类也有@Beta的注解，所以要谨慎使用。
        InetAddress inetAddress = InetAddresses.forUriString("127.0.0.1");
        System.out.println(inetAddress.getHostAddress());
        System.out.println(inetAddress.getHostName());
    }

    @Test
    public void testGuavaMarh() {
        int logFloor = LongMath.log2(100, FLOOR);
        int mustNotOverflow = IntMath.checkedMultiply(100, 200);
        long quotient = LongMath.divide(9, 3, RoundingMode.UNNECESSARY); // fail fast on non-multiple of 3
        BigInteger nearestInteger = DoubleMath.roundToBigInteger(100.545, RoundingMode.HALF_EVEN);
        BigInteger sideLength = BigIntegerMath.sqrt(BigInteger.TEN, CEILING);

    }


    @Test
    public void testHash() {
        HashFunction hf = Hashing.md5();
        Person person = Person.create(1, "liu");
        Funnel<Person> personFunnel = new Funnel<Person>() {
            @Override
            public void funnel(Person person, PrimitiveSink primitiveSink) {
                primitiveSink
                        .putLong(person.getId())
                        .putString(person.getName(), Charsets.UTF_8);
                // 注：putString(“abc”, Charsets.UTF_8).putString(“def”, Charsets.UTF_8)完全等同于putString(“ab”, Charsets.UTF_8).putString(“cdef”, Charsets.UTF_8)，
                // 因为它们提供了相同的字节序列。这可能带来预料之外的散列冲突。增加某种形式的分隔符有助于消除散列冲突。
            }
        };
        HashCode hc = hf.newHasher()
                .putLong(person.getId())
                .putString(person.getName(), Charsets.UTF_8)
                .putObject(person, personFunnel)
                .hash();
        System.out.println(hc);

//        布鲁姆过滤器[BloomFilter]
//        布鲁姆过滤器是哈希运算的一项优雅运用，它可以简单地基于Object.hashCode() 实现。简而言之，布鲁姆过滤器是一种概率数据结构，它允许你检测某个对象是一定不在过滤器中，还是可能已经添加到过滤器了。
//        布鲁姆过滤器的维基页面对此作了全面的介绍，同时我们推荐github中的一个教程。
//        Guava散列包有一个内建的布鲁姆过滤器实现，你只要提供Funnel就可以使用它。你可以使用create(Funnel funnel, int expectedInsertions, double falsePositiveProbability)方法获取BloomFilter<T>，缺省误检率[falsePositiveProbability] 为3 %。
//        BloomFilter<T> 提供了boolean mightContain(T) 和void put (T)，它们的含义都不言自明了。
        List<Person> friendsList = Lists.newArrayListWithCapacity(10);
        for (int i = 0; i < 10; i++) {
            friendsList.add(Person.create(i + 1, "friend" + ('A' + i)));
        }
        BloomFilter<Person> friends = BloomFilter.create(personFunnel, 500, 0.01);
        for (Person friend : friendsList) {
            friends.put(friend);
        }
        Person dude = Person.create(100, "dude");
        // 很久以后
        if (friends.mightContain(dude)) {
            System.out.println("hhhh");
            //dude不是朋友还运行到这里的概率为1%
            //在这儿，我们可以在做进一步精确检查的同时触发一些异步加载
        }
//        Hashing类
//        Hashing类提供了若干散列函数，以及运算HashCode对象的工具方法。
//        已提供的散列函数
//        md5()	murmur3_128()	murmur3_32()	sha1()
//        sha256()	sha512()	goodFastHash(int bits)
//        HashCode运算
//        方法	描述
//        HashCode combineOrdered( Iterable<HashCode>)	以有序方式联接散列码，如果两个散列集合用该方法联接出的散列码相同，那么散列集合的元素可能是顺序相等的
//        HashCode   combineUnordered( Iterable<HashCode>)	以无序方式联接散列码，如果两个散列集合用该方法联接出的散列码相同，那么散列集合的元素可能在某种排序下是相等的
//        int   consistentHash( HashCode, int buckets)	为给定的”桶”大小返回一致性哈希值。当”桶”增长时，该方法保证最小程度的一致性哈希值变化。详见一致性哈希。
    }
}

class Person {
    private long id;
    private String name;

    public static Person create() {
        return new Person();
    }

    public static Person create(long id, String name) {
        return new Person(id, name);
    }

    private Person() {
    }

    private Person(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return new StringBuilder("Person:[id=")
                .append(id)
                .append(",name=")
                .append(name)
                .append("]").toString();
    }
}